﻿using System;
using Castle.MicroKernel.Facilities;
using Castle.MicroKernel.Registration;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Cfg.MappingSchema;
using ConfOrm;
using ConfOrm.NH;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using NHibernate.Mapping.ByCode;
using System.Collections.Generic;

namespace MyStore.PresentationModel.Facilities
{
    public class PersistenceFacility : AbstractFacility
    {
        protected override void Init()
        {
            var config = BuildDatabaseConfiguration();

            Kernel.Register(
                Component.For<ISessionFactory>()
                    .UsingFactoryMethod(_ => config.BuildSessionFactory()).LifeStyle.Singleton,
                Component.For<ISession>()
                    .UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession()).LifeStyle.PerWebRequest);
        }


        private const string CON_STR = "Data Source=192.168.7.100:1521/eshop;User ID=eshop;Password=TY2D4M#g24!;Min Pool Size=15;Max Pool Size=50;";

        private NHibernate.Cfg.Configuration BuildDatabaseConfiguration()
        {
            Configuration cfg = LoadConfigurationFromFile();
            if (cfg == null)
            {
                cfg = new NHibernate.Cfg.Configuration()
          .DataBaseIntegration(db =>
          {
              db.Dialect<Oracle10gDialect>();
              db.Driver<OracleClientDriver>();
              db.ConnectionString = CON_STR;
              db.LogSqlInConsole = true;
              db.LogFormattedSql = true;
          });

                cfg.AddDeserializedMapping(GetMapping(), "eshop");
                SaveConfigurationToFile(cfg);
            }
            return cfg;
        }

        private HbmMapping GetMapping()
        {
            var orm = new ObjectRelationalMapper();

            var entities = new List<Type> { typeof(MyStore.Members.Member) };

            orm.TablePerClass(entities);

            var mapper = new Mapper(orm);

            mapper.Class<MyStore.Members.Member>(cm =>
            {
                cm.Table("MBR_MEMBERS");
                cm.Id(o => o.UserName, m => m.Generator(Generators.Assigned));
                cm.Id(o => o.UserName, m => m.Column("EMAIL"));
                cm.Property(o => o.Password, m => m.Column("PASSWORD"));
            });

            return mapper.CompileMappingFor(entities);
        }

        #region 使用序列化降低启用成本 主要测试
        private static readonly string SERIALIZD_CFG = "configuration.bin";

        public static string Serialize(HbmMapping hbmElement)
        {
            var setting = new XmlWriterSettings { Indent = true };
            var serializer = new XmlSerializer(typeof(HbmMapping));
            using (var memStream = new MemoryStream(2048))
            using (var xmlWriter = XmlWriter.Create(memStream, setting))
            {
                serializer.Serialize(xmlWriter, hbmElement);
                memStream.Flush();
                memStream.Position = 0;
                var sr = new StreamReader(memStream);
                return sr.ReadToEnd();
            }
        }

        private static Configuration LoadConfigurationFromFile()
        {
            if (!IsConfigurationgFileValid())
                return null;
            try
            {
                using (var file = File.Open(SERIALIZD_CFG, FileMode.Open))
                {
                    var bf = new BinaryFormatter();
                    return bf.Deserialize(file) as Configuration;
                }
            }
            catch (Exception e)
            {
                return null;
            }
        }

        private static bool IsConfigurationgFileValid()
        {
            if (!File.Exists(SERIALIZD_CFG))
                return false;
            var configInfo = new FileInfo(SERIALIZD_CFG);
            var asm = Assembly.GetExecutingAssembly();
            if (asm.Location == null)
                return false;
            var asmInfo = new FileInfo(asm.Location);
            if (asmInfo.LastWriteTime > configInfo.LastWriteTime)
                return false;
            var appDomain = AppDomain.CurrentDomain;
            var appConfigPath = appDomain.SetupInformation.ConfigurationFile;
            var appConfigInfo = new FileInfo(appConfigPath);
            if (appConfigInfo.LastWriteTime > configInfo.LastWriteTime)
                return false;
            return true;
        }

        private static void SaveConfigurationToFile(Configuration cfg)
        {
            using (var file = File.Open(SERIALIZD_CFG, FileMode.Create))
            {
                var bf = new BinaryFormatter();
                bf.Serialize(file, cfg);
            }
        }

        #endregion

    }
}